import {BaseClient, Config, Resp} from 'http-util.js';

function createResp(config, xhr){
	return new Resp({
		config,
		rawResp:xhr,
		url:xhr.responseURL,
		status:xhr.status,
		statusText:xhr.statusText,
		headers:new Headers(
				xhr.getAllResponseHeaders().trim()
					 .split(/[\r\n]+/)
					 .filter(x=>x) // 不为空串
					 .map(x=>x.split(': ', 2)), // 防止值里面出现`: `
		),
	});
}

// 根据配置创建xhr对象，返回一个已经open的xhr对象
function createXHL(config){
	const {
		onUploading, onDownloading,
		rawOptions,
	} = config;
	const href = config.getHref();
	const method = config.getMethod();
	const headers = config.getHeaders();
	
	// 构建xhr，并发起请求
	const xhr = new XMLHttpRequest();
	xhr.open(method, href);
	
	// 在open之后设置选项
	Object.assign(xhr, rawOptions); // 其他请求选项
	headers.forEach((v, k)=>xhr.setRequestHeader(k, v)); // 设置请求头
	xhr.responseType = 'arraybuffer'; // 接收arraybuffer，用于转换成流
	
	// 监控下载和上传进度
	if (onUploading){
		xhr.upload.onprogress = e=>{
			onUploading(e.loaded, e.lengthComputable? e.total: null);
		};
	}
	if (onDownloading){
		xhr.onprogress = e=>{
			onDownloading(e.loaded, e.lengthComputable? e.total: null);
		};
	}
	return xhr;
}

export class XHRClient extends BaseClient{
	constructor(config){
		super(config);
	}
	
	_send({checkStatus = false, signal} = {}){
		const config = this.config.clone();
		// beforeSend 会调用两次，先后接收Config和XHR对象
		const configPromise = (async()=>{
			const {onBeforeSend} = config;
			if (!onBeforeSend){
				return createXHL(config); // 如果没有onBeforeSend，直接创建xhr对象
			}
			
			await onBeforeSend(config); // 触发请求前的回调函数
			const xhr = createXHL(config); // 创建xhr对象
			await onBeforeSend(xhr); // 再次触发，可以注册事件监听器
			return xhr;
		})();
		
		// 等待header和body的Promise
		const {promise, reject, resolve} = Promise.withResolvers(); // 收到响应头时的Promise
		
		(async()=>{
			const xhr = await configPromise; // 等待xhr对象
			
			let controller;
			const stream = new ReadableStream({
				start(ctrl){
					controller = ctrl;
				},
			});
			
			// 注册事件监听器
			xhr.onreadystatechange = async()=>{
				if (xhr.readyState===XMLHttpRequest.HEADERS_RECEIVED){ // 收到响应头，返回响应对象
					const status = xhr.status; // 响应状态码
					if (checkStatus && !(200<=status && status<400)){ // 检查状态码是否在[200,400)范围内
						xhr.abort(); // 中断请求
						reject(this._newStatusError(status));
						return;
					}
					const resp = createResp(config, xhr);
					resp._body = stream;
					
					// 设置好bodyStream之后，调用 onReceive回调函数
					if (config.onReceive){
						await config.onReceive(resp); // 传入响应对象
					}
					resolve(resp);
				}
			};
			
			xhr.onload = async()=>{
				controller.enqueue(new Uint8Array(xhr.response));
				controller.close();
			};
			xhr.onerror = ()=>reject(new Error('error')); // 发生网络层面的错误
			xhr.onabort = ()=>reject(new Error('abort')); // 请求被中止
			xhr.ontimeout = ()=>reject(new Error('timeout')); // 请求超时
			
			// 检查是否中断
			if (signal){
				if (signal.aborted){
					xhr.abort();
				} else {
					signal.onabort = ()=>xhr.abort(); // 监控请求中断
					xhr.send(config.data); // 发起请求
				}
			} else {
				xhr.send(config.data); // 发起请求
			}
		})();
		
		return promise;
	}
}

// 简化使用的对外的接口
export function req(url, options){
	// 创建Client并返回
	return new XHRClient(new Config({url, ...options}));
}
